using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

using NSimpleDB.Service.VistaDb.QueryProcessing.Compiler.AST;

namespace NSimpleDB.Service.VistaDb.QueryProcessing.Compiler
{
    internal class SqlCodeGenerator
    {
        // translate
        //      predicate0 "union" predicate1 "union" ...
        // into
        //      select distinct itemName from <domain tablename> where
        //          (<itemSetTerm>) OR (<itemSetTerm>) OR ...
        public string GenerateQueryCode(string domainTablename, Query q)
        {
            StringBuilder sql = new StringBuilder("select distinct itemName from $$$domainTablename$$$ where (");

            for (int i = 0; i < q.ItemSetTermList.Length; i++)
            {
                if (i > 0)
                    sql.Append(") OR (");
                GenerateItemSetTermCode(sql, q.ItemSetTermList[i]);
            }
            sql.Append(")");

            return sql.Replace("$$$domainTablename$$$", domainTablename).ToString();
        }


        // translate
        //      predicate0 "intersection" predicate1 "intersection" ...
        // into
        //      itemName in (select itemName from <domain tablename> where 
        //         <predicateExpr> AND itemName in (select itemName from <domain tablename> where
        //                                <predicateExpr>
        //                                )
        //         )
        private void GenerateItemSetTermCode(StringBuilder sql, ItemSetTerm ist)
        {
            if (ist.ItemSetFactorList.Length > 1)
            {
                for (int i = 0; i < ist.ItemSetFactorList.Length; i++)
                {
                    if (i > 0) sql.Append(" AND ");
                    sql.Append("itemName in (select itemName from $$$domainTablename$$$ where ");
                    GeneratePredicateExpressionCode(sql, ist.ItemSetFactorList[i].Predicate, ist.ItemSetFactorList[i].Negate);
                }
                sql.Append("".PadLeft(ist.ItemSetFactorList.Length, ')'));
            }
            else
                GeneratePredicateExpressionCode(sql, ist.ItemSetFactorList[0].Predicate, ist.ItemSetFactorList[0].Negate);
        }


        // translate
        //      [ "not" ] "[" c0 "or" c1 "or" ... "]"
        // into
        //      [ not ] ( <predicateTerm> OR <predicateTerm> OR ... )
        private void GeneratePredicateExpressionCode(StringBuilder sql, PredicateExpression pexp, bool negate)
        {
            if (negate) sql.Append(" NOT ");
            sql.Append("(");

            for (int i = 0; i < pexp.PredicateTermList.Length; i++)
            {
                if (i > 0) sql.Append(" OR ");
                GeneratePredicateTermCode(sql, pexp.PredicateTermList[i]);
            }

            sql.Append(")");
        }


        // translate
        //      c0 "and" c1 "and" ...
        // into
        //      <predicateFactor> AND itemName in (select itemName from <domain tablename> where
        //          <predicateFactor> AND itemName in (select ...
        //                                             )
        //                                        )
        private void GeneratePredicateTermCode(StringBuilder sql, PredicateTerm pt)
        {
            if (pt.PredicateFactorList.Length > 1)
            {
                for (int i = 0; i < pt.PredicateFactorList.Length; i++)
                {
                    if (i > 0) sql.Append(" AND itemName in (select itemName from $$$domainTablename$$$ where ");
                    GeneratePredicateFactorCode(sql, pt.PredicateFactorList[i]);
                }
                sql.Append("".PadLeft(pt.PredicateFactorList.Length - 1, ')'));
            }
            else
                GeneratePredicateFactorCode(sql, pt.PredicateFactorList[0]);
        }

        
        // translate
        //      attrName op attrValue
        // into
        //      [ not ] attributeName=<attrName> and attributeValue <op> <attrValue>
        private void GeneratePredicateFactorCode(StringBuilder sql, PredicateFactor pf)
        {
            if (pf.Negate)
                sql.Append(" NOT (");
            else
                sql.Append("(");

            string value = pf.Comparison.AttributeValue;
            string op = "=";
            switch(pf.Comparison.ComparisonOp)
            {
                case QueryTokensTypes.EqualOp: op = "="; break;
                case QueryTokensTypes.NotEqualOp: op = "!="; break;
                case QueryTokensTypes.LargerOp: op = ">"; break;
                case QueryTokensTypes.LargerOrEqualOp: op = ">="; break;
                case QueryTokensTypes.LessOp: op = "<"; break;
                case QueryTokensTypes.LessOrEqualOp: op = "<="; break;
                case QueryTokensTypes.StartsWithOp:
                    op = " like ";
                    value = value.Substring(0, value.Length - 1) + "%'";
                    break;
            }

            sql.Append(string.Format(
                "attributeName={0} and attributeValue{1}{2}",
                pf.Comparison.AttributeName,
                op,
                value
                ));

            sql.Append(")");
        }
    }
}
